#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>

#pragma pack(push,1) // 1 字节对齐，与服务器保持一致
typedef struct{
    char name[100]; //文件名
    uint64_t size;  //文件大小
}file_info;
#pragma pack(pop)

// 接收指定长度的数据
int recv_data(int sock, uint64_t size, char* buff) {
    uint64_t recv_cnt = 0;
    ssize_t ret;
    
    while(recv_cnt < size) {
        ret = recv(sock, buff + recv_cnt, size - recv_cnt, 0);
        if(ret <= 0) {
            if(ret == 0) {
                printf("连接被服务器关闭\n");
            } else {
                perror("recv");
            }
            break;
        }
        recv_cnt += ret;
    }
    
    return recv_cnt;
}

int main(int argc, char** argv){
    if(argc != 4){
        fprintf(stderr, "Usage: %s <server_ip> <server_port> <save_directory>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    //1.创建套接字
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock == -1){
        perror("socket");
        exit(EXIT_FAILURE);
    }

    //2.连接服务器
    struct sockaddr_in srv_addr;
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_addr.s_addr = inet_addr(argv[1]);
    srv_addr.sin_port = htons(atoi(argv[2]));

    if(connect(sock, (struct sockaddr*)&srv_addr, sizeof(srv_addr)) == -1){
        perror("connect");
        close(sock);
        exit(EXIT_FAILURE);
    }

    printf("连接服务器 %s:%s 成功！\n", argv[1], argv[2]);

    //3.创建保存目录（如果不存在）
    char save_dir[256];
    snprintf(save_dir, sizeof(save_dir), "%s", argv[3]);
    
    // 简单的目录存在性检查
    if(access(save_dir, F_OK) == -1) {
        printf("目录 %s 不存在，将直接保存文件到当前目录\n", save_dir);
        snprintf(save_dir, sizeof(save_dir), ".");
    }

    //4.接收多个文件
    int file_count = 0;
    char file_path[512];
    
    while(1) {
        // 接收文件信息结构体
        file_info fi;
        ssize_t info_size = recv_data(sock, sizeof(fi), (char*)&fi);
        
        if(info_size == 0) {
            printf("服务器正常关闭连接\n");
            break;
        }
        
        if(info_size != sizeof(fi)) {
            printf("文件信息接收不完整（期望 %lu bytes，实际 %ld bytes）\n", 
                   sizeof(fi), info_size);
            break;
        }
        
        // 检查是否为结束标记（文件名为空表示结束）
        if(fi.name[0] == '\0' || fi.size == 0) {
            printf("所有文件接收完成\n");
            break;
        }
        
        file_count++;
        printf("\n正在接收第 %d 个文件:\n", file_count);
        printf("文件名: %s\n", fi.name);
        printf("文件大小: %lu bytes\n", fi.size);
        
        // 创建完整的文件路径
        if(strcmp(save_dir, ".") == 0) {
            snprintf(file_path, sizeof(file_path), "%s", fi.name);
        } else {
            snprintf(file_path, sizeof(file_path), "%s/%s", save_dir, fi.name);
        }
        
        // 检查文件是否已存在
        if(access(file_path, F_OK) == 0) {
            printf("警告: 文件 %s 已存在，将被覆盖\n", file_path);
        }
        
        // 创建文件
        int fd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if(fd == -1){
            perror("open");
            printf("无法创建文件 %s，跳过此文件\n", file_path);
            // 跳过这个文件的数据
            lseek(sock, fi.size, SEEK_CUR); // 注意：这不是标准做法，最好实际读取并丢弃数据
            continue;
        }
        
        // 接收文件数据
        uint64_t total_received = 0;
        char buff[8192]; // 使用更大的缓冲区提高效率
        
        while(total_received < fi.size) {
            size_t to_read = sizeof(buff);
            if(fi.size - total_received < to_read) {
                to_read = fi.size - total_received;
            }
            
            ssize_t ret = recv_data(sock, to_read, buff);
            if(ret <= 0) {
                printf("文件 %s 接收中断\n", fi.name);
                break;
            }
            
            ssize_t written = write(fd, buff, ret);
            if(written != ret) {
                perror("write");
                close(fd);
                break;
            }
            
            total_received += ret;
            
            // 显示进度
            if(fi.size > 0) {
                int progress = (int)((total_received * 100) / fi.size);
                printf("\r接收进度: %d%% [%lu/%lu bytes]", progress, total_received, fi.size);
                fflush(stdout);
            }
        }
        
        close(fd);
        
        if(total_received == fi.size) {
            printf("\n文件 %s 接收完成！\n", fi.name);
        } else {
            printf("\n文件 %s 接收不完整（期望 %lu bytes，实际接收 %lu bytes）\n", 
                   fi.name, fi.size, total_received);
            
            // 删除不完整的文件
            unlink(file_path);
        }
    }
    
    if(file_count == 0) {
        printf("没有接收到任何文件\n");
    } else {
        printf("\n总共接收了 %d 个文件，保存到目录: %s\n", file_count, save_dir);
    }
    
    //5.关闭连接
    close(sock);
    printf("客户端退出\n");
    
    return 0;
}



